/*
    Geocache: some utilities for managing and visualizing geocache information
    Copyright (C) 2008  Gary Jackson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.thegbomb.sphere;

/**
 * This class represents points in R**3, as well as a number of operations on these points.  We also treat these points as vectors for some operations.
 */
public class Cartesian {
    private double x, y, z;
    
    /**
     * Create a point in cartesian space that coincides with a point on the unit sphere.
     * @param p any point on the unit sphere
     */
    public Cartesian(Point p) {
        this.x = Math.cos(p.getTheta()) * Math.cos(p.getPhi());
        this.y = Math.cos(p.getTheta()) * Math.sin(p.getPhi());
        this.z = Math.sin(p.getTheta());
    }
    
    /**
     * Create a point in cartesian space with the given coordinates.
     * @param x the X coordinate
     * @param y the Y coordinate
     * @param z the Z coordinate
     */
    public Cartesian(double x, double y, double z) {
        this.x = x; this.y = y; this.z = z;
    }
    
    /**
     * Get the X coordinate for this cartesian point.
     * @return this.z
     */
    public double getX() { return this.x; }
    
    /**
     * Get the Y coordinate for this cartesian point.
     * @return this.y
     */
    public double getY() { return this.y; }
    
    /**
     * Get the Z coordinate for this cartesian point.
     * @return this.x
     */
    public double getZ() { return this.z; }
    
    /**
     * Multiply matrix X this and return the result as a cartesian point.
     * @param matrix a 3x3 matrix of double values
     * @return the new point that is the result of computing the matrix product of matrix and this.
     * @throws SphereException if the matrix is not the correct dimension
     */
    public Cartesian postmultiply(double matrix[][]) {
        if (matrix.length != 3 && matrix[0].length != 3)
            throw new SphereException("matrix has incorrect dimensions");
        
        return new Cartesian(matrix[0][0] * this.x + matrix[0][1] * this.y + matrix[0][2] * this.z,
                matrix[1][0] * this.x + matrix[1][1] * this.y + matrix[1][2] * this.z,
                matrix[2][0] * this.x + matrix[2][1] * this.y + matrix[2][2] * this.z);
    }
    
    /**
     * Compute the cross product of this and b.  The cross product of two vectors yields a new vector orthogonal to the two operands.
     * @param b Any cartesian point.  If this == b, then the result will be the zero vector.  If this is very close to b, then the results will likely be unsatisfactory.
     * @return the cross product this X b.
     */
    public Cartesian cross(Cartesian b) {
        Cartesian a = this;
        Cartesian c = new Cartesian(a.getY()*b.getZ() - a.getZ()*b.getY(), a.getZ()*b.getX() - a.getX()*b.getZ(), a.getX()*b.getY() - a.getY()*b.getX());
        
        return c;
    }
    
    /**
     * Compute the dot product of this and b.  The dot product is the sum of the products of each element.  For instance, (t, u, v).(x, y, z) = t*x + u*y + v*z.
     * @param b any cartesian point
     * @return the dot product this . b.
     */
    public double dot(Cartesian b) {
        return x*b.x + y*b.y + z*b.z;
    }
    
    /**
     * Compute the normalized form of this point: the distance from the origin to a normalized point is 1.  The normalized form is computed by doing the scalar division of the point by its magnitude: this/magnitude(this).  The normalized point lies on the surface of the unit sphere.
     * @return normalized form of this
     */
    public Cartesian normalize() {
        return this.sdiv(this.magnitude());
    }
    
    /**
     * Compute distance from the origin to this point.  The magnitude is the square root of the dot product of the vector by itself: sqrt(this . this).
     * @return the distance from (0, 0, 0) to this.
     */
    public double magnitude() {
        return Math.sqrt(this.dot(this));
    }
    
    /**
     * Compute the vector sum this + b. For two vectors (t, u, v) and (x, y, z), the sum is (t+x, u+y, v+z).
     * @param b any cartesian point
     * @return the sum of vectors this and b 
     */
    public Cartesian add(Cartesian b) {
        return new Cartesian(x+b.x, y+b.y, z+b.z);
    }
    
    /**
     * Compute scalar multiplication.
     * @param s scalar operand
     * @return this*s
     */
    public Cartesian smult(double s) {
        return new Cartesian(s*x, s*y, s*z);
    }
    
    /**
     * Compute scalar division.
     * @param s scalar divisor
     * @return this/s
     */
    public Cartesian sdiv(double s) {
        return new Cartesian(x/s, y/s, z/s);
    }
    
    /**
     * Converts this cartesian point in to a human readable form.
     * @return "(X, Y, Z)", where X, Y, and Z are replaced by the decimal representations of those coordinates.
     */
    public String toString() {
        return "(" + x + ", " + y + ", " + z + ")";
    }
}


